home *** CD-ROM | disk | FTP | other *** search
/ OpenGL Superbible (2nd Edition) / OpenGL SuperBible e2.iso / tools / GLUT-3.7 / PROGS / DEMOS / SMOOTH / GLM.C next >
Encoding:
C/C++ Source or Header  |  1998-08-12  |  54.1 KB  |  1,887 lines

  1. /*    
  2.  *  GLM library.  Wavefront .obj file format reader/writer/manipulator.
  3.  *
  4.  *  Written by Nate Robins, 1997.
  5.  *  email: ndr@pobox.com
  6.  *  www: http://www.pobox.com/~ndr
  7.  */
  8.  
  9. /* includes */
  10. #include <math.h>
  11. #include <stdio.h>
  12. #include <string.h>
  13. #include <stdlib.h>
  14. #include <assert.h>
  15. #include "glm.h"
  16.  
  17. /* Some <math.h> files do not define M_PI... */
  18. #ifndef M_PI
  19. #define M_PI 3.14159265358979323846
  20. #endif
  21.  
  22. /* defines */
  23. #define T(x) model->triangles[(x)]
  24.  
  25.  
  26. /* enums */
  27. enum { X, Y, Z, W };            /* elements of a vertex */
  28.  
  29.  
  30. /* typedefs */
  31.  
  32. /* _GLMnode: general purpose node
  33.  */
  34. typedef struct _GLMnode {
  35.   GLuint           index;
  36.   GLboolean        averaged;
  37.   struct _GLMnode* next;
  38. } GLMnode;
  39.  
  40. /* strdup is actually not a standard ANSI C or POSIX routine
  41.    so implement a private one.  OpenVMS does not have a strdup; Linux's
  42.    standard libc doesn't declare strdup by default (unless BSD or SVID
  43.    interfaces are requested). */
  44. static char *
  45. stralloc(const char *string)
  46. {
  47.   char *copy;
  48.  
  49.   copy = malloc(strlen(string) + 1);
  50.   if (copy == NULL)
  51.     return NULL;
  52.   strcpy(copy, string);
  53.   return copy;
  54. }
  55.  
  56. /* private functions */
  57.  
  58. /* _glmMax: returns the maximum of two floats */
  59. static GLfloat
  60. _glmMax(GLfloat a, GLfloat b) 
  61. {
  62.   if (a > b)
  63.     return a;
  64.   return b;
  65. }
  66.  
  67. /* _glmAbs: returns the absolute value of a float */
  68. static GLfloat
  69. _glmAbs(GLfloat f)
  70. {
  71.   if (f < 0)
  72.     return -f;
  73.   return f;
  74. }
  75.  
  76. /* _glmDot: compute the dot product of two vectors
  77.  *
  78.  * u - array of 3 GLfloats (GLfloat u[3])
  79.  * v - array of 3 GLfloats (GLfloat v[3])
  80.  */
  81. static GLfloat
  82. _glmDot(GLfloat* u, GLfloat* v)
  83. {
  84.   assert(u);
  85.   assert(v);
  86.  
  87.   /* compute the dot product */
  88.   return u[X] * v[X] + u[Y] * v[Y] + u[Z] * v[Z];
  89. }
  90.  
  91. /* _glmCross: compute the cross product of two vectors
  92.  *
  93.  * u - array of 3 GLfloats (GLfloat u[3])
  94.  * v - array of 3 GLfloats (GLfloat v[3])
  95.  * n - array of 3 GLfloats (GLfloat n[3]) to return the cross product in
  96.  */
  97. static GLvoid
  98. _glmCross(GLfloat* u, GLfloat* v, GLfloat* n)
  99. {
  100.   assert(u);
  101.   assert(v);
  102.   assert(n);
  103.  
  104.   /* compute the cross product (u x v for right-handed [ccw]) */
  105.   n[X] = u[Y] * v[Z] - u[Z] * v[Y];
  106.   n[Y] = u[Z] * v[X] - u[X] * v[Z];
  107.   n[Z] = u[X] * v[Y] - u[Y] * v[X];
  108. }
  109.  
  110. /* _glmNormalize: normalize a vector
  111.  *
  112.  * n - array of 3 GLfloats (GLfloat n[3]) to be normalized
  113.  */
  114. static GLvoid
  115. _glmNormalize(GLfloat* n)
  116. {
  117.   GLfloat l;
  118.  
  119.   assert(n);
  120.  
  121.   /* normalize */
  122.   l = (GLfloat)sqrt(n[X] * n[X] + n[Y] * n[Y] + n[Z] * n[Z]);
  123.   n[0] /= l;
  124.   n[1] /= l;
  125.   n[2] /= l;
  126. }
  127.  
  128. /* _glmEqual: compares two vectors and returns GL_TRUE if they are
  129.  * equal (within a certain threshold) or GL_FALSE if not. An epsilon
  130.  * that works fairly well is 0.000001.
  131.  *
  132.  * u - array of 3 GLfloats (GLfloat u[3])
  133.  * v - array of 3 GLfloats (GLfloat v[3]) 
  134.  */
  135. static GLboolean
  136. _glmEqual(GLfloat* u, GLfloat* v, GLfloat epsilon)
  137. {
  138.   if (_glmAbs(u[0] - v[0]) < epsilon &&
  139.       _glmAbs(u[1] - v[1]) < epsilon &&
  140.       _glmAbs(u[2] - v[2]) < epsilon) 
  141.   {
  142.     return GL_TRUE;
  143.   }
  144.   return GL_FALSE;
  145. }
  146.  
  147. /* _glmWeldVectors: eliminate (weld) vectors that are within an
  148.  * epsilon of each other.
  149.  *
  150.  * vectors    - array of GLfloat[3]'s to be welded
  151.  * numvectors - number of GLfloat[3]'s in vectors
  152.  * epsilon    - maximum difference between vectors 
  153.  *
  154.  */
  155. GLfloat*
  156. _glmWeldVectors(GLfloat* vectors, GLuint* numvectors, GLfloat epsilon)
  157. {
  158.   GLfloat* copies;
  159.   GLuint   copied;
  160.   GLuint   i, j;
  161.  
  162.   copies = (GLfloat*)malloc(sizeof(GLfloat) * 3 * (*numvectors + 1));
  163.   memcpy(copies, vectors, (sizeof(GLfloat) * 3 * (*numvectors + 1)));
  164.  
  165.   copied = 1;
  166.   for (i = 1; i <= *numvectors; i++) {
  167.     for (j = 1; j <= copied; j++) {
  168.       if (_glmEqual(&vectors[3 * i], &copies[3 * j], epsilon)) {
  169.     goto duplicate;
  170.       }
  171.     }
  172.  
  173.     /* must not be any duplicates -- add to the copies array */
  174.     copies[3 * copied + 0] = vectors[3 * i + 0];
  175.     copies[3 * copied + 1] = vectors[3 * i + 1];
  176.     copies[3 * copied + 2] = vectors[3 * i + 2];
  177.     j = copied;                /* pass this along for below */
  178.     copied++;
  179.  
  180.   duplicate:
  181.     /* set the first component of this vector to point at the correct
  182.        index into the new copies array */
  183.     vectors[3 * i + 0] = (GLfloat)j;
  184.   }
  185.  
  186.   *numvectors = copied-1;
  187.   return copies;
  188. }
  189.  
  190. /* _glmFindGroup: Find a group in the model
  191.  */
  192. GLMgroup*
  193. _glmFindGroup(GLMmodel* model, char* name)
  194. {
  195.   GLMgroup* group;
  196.  
  197.   assert(model);
  198.  
  199.   group = model->groups;
  200.   while(group) {
  201.     if (!strcmp(name, group->name))
  202.       break;
  203.     group = group->next;
  204.   }
  205.  
  206.   return group;
  207. }
  208.  
  209. /* _glmAddGroup: Add a group to the model
  210.  */
  211. GLMgroup*
  212. _glmAddGroup(GLMmodel* model, char* name)
  213. {
  214.   GLMgroup* group;
  215.  
  216.   group = _glmFindGroup(model, name);
  217.   if (!group) {
  218.     group = (GLMgroup*)malloc(sizeof(GLMgroup));
  219.     group->name = stralloc(name);
  220.     group->material = 0;
  221.     group->numtriangles = 0;
  222.     group->triangles = NULL;
  223.     group->next = model->groups;
  224.     model->groups = group;
  225.     model->numgroups++;
  226.   }
  227.  
  228.   return group;
  229. }
  230.  
  231. /* _glmFindGroup: Find a material in the model
  232.  */
  233. GLuint
  234. _glmFindMaterial(GLMmodel* model, char* name)
  235. {
  236.   GLuint i;
  237.  
  238.   for (i = 0; i < model->nummaterials; i++) {
  239.     if (!strcmp(model->materials[i].name, name))
  240.       goto found;
  241.   }
  242.  
  243.   /* didn't find the name, so set it as the default material */
  244.   printf("_glmFindMaterial():  can't find material \"%s\".\n", name);
  245.   i = 0;
  246.  
  247. found:
  248.   return i;
  249. }
  250.  
  251.  
  252. /* _glmDirName: return the directory given a path
  253.  *
  254.  * path - filesystem path
  255.  *
  256.  * The return value should be free'd.
  257.  */
  258. static char*
  259. _glmDirName(char* path)
  260. {
  261.   char* dir;
  262.   char* s;
  263.  
  264.   dir = stralloc(path);
  265.  
  266.   s = strrchr(dir, '/');
  267.   if (s)
  268.     s[1] = '\0';
  269.   else
  270.     dir[0] = '\0';
  271.  
  272.   return dir;
  273. }
  274.  
  275.  
  276. /* _glmReadMTL: read a wavefront material library file
  277.  *
  278.  * model - properly initialized GLMmodel structure
  279.  * name  - name of the material library
  280.  */
  281. static GLvoid
  282. _glmReadMTL(GLMmodel* model, char* name)
  283. {
  284.   FILE* file;
  285.   char* dir;
  286.   char* filename;
  287.   char  buf[128];
  288.   GLuint nummaterials, i;
  289.  
  290.   dir = _glmDirName(model->pathname);
  291.   filename = (char*)malloc(sizeof(char) * (strlen(dir) + strlen(name) + 1));
  292.   strcpy(filename, dir);
  293.   strcat(filename, name);
  294.   free(dir);
  295.  
  296.   /* open the file */
  297.   file = fopen(filename, "r");
  298.   if (!file) {
  299.     fprintf(stderr, "_glmReadMTL() failed: can't open material file \"%s\".\n",
  300.         filename);
  301.     exit(1);
  302.   }
  303.   free(filename);
  304.  
  305.   /* count the number of materials in the file */
  306.   nummaterials = 1;
  307.   while(fscanf(file, "%s", buf) != EOF) {
  308.     switch(buf[0]) {
  309.     case '#':                /* comment */
  310.       /* eat up rest of line */
  311.       fgets(buf, sizeof(buf), file);
  312.       break;
  313.     case 'n':                /* newmtl */
  314.       fgets(buf, sizeof(buf), file);
  315.       nummaterials++;
  316.       sscanf(buf, "%s %s", buf, buf);
  317.       break;
  318.     default:
  319.       /* eat up rest of line */
  320.       fgets(buf, sizeof(buf), file);
  321.       break;
  322.     }
  323.   }
  324.  
  325.   rewind(file);
  326.  
  327.   /* allocate memory for the materials */
  328.   model->materials = (GLMmaterial*)malloc(sizeof(GLMmaterial) * nummaterials);
  329.   model->nummaterials = nummaterials;
  330.  
  331.   /* set the default material */
  332.   for (i = 0; i < nummaterials; i++) {
  333.     model->materials[i].name = NULL;
  334.     model->materials[i].shininess = 0;
  335.     model->materials[i].diffuse[0] = 0.8;
  336.     model->materials[i].diffuse[1] = 0.8;
  337.     model->materials[i].diffuse[2] = 0.8;
  338.     model->materials[i].diffuse[3] = 1.0;
  339.     model->materials[i].ambient[0] = 0.2;
  340.     model->materials[i].ambient[1] = 0.2;
  341.     model->materials[i].ambient[2] = 0.2;
  342.     model->materials[i].ambient[3] = 1.0;
  343.     model->materials[i].specular[0] = 0.0;
  344.     model->materials[i].specular[1] = 0.0;
  345.     model->materials[i].specular[2] = 0.0;
  346.     model->materials[i].specular[3] = 1.0;
  347.   }
  348.   model->materials[0].name = stralloc("default");
  349.  
  350.   /* now, read in the data */
  351.   nummaterials = 0;
  352.   while(fscanf(file, "%s", buf) != EOF) {
  353.     switch(buf[0]) {
  354.     case '#':                /* comment */
  355.       /* eat up rest of line */
  356.       fgets(buf, sizeof(buf), file);
  357.       break;
  358.     case 'n':                /* newmtl */
  359.       fgets(buf, sizeof(buf), file);
  360.       sscanf(buf, "%s %s", buf, buf);
  361.       nummaterials++;
  362.       model->materials[nummaterials].name = stralloc(buf);
  363.       break;
  364.     case 'N':
  365.       fscanf(file, "%f", &model->materials[nummaterials].shininess);
  366.       /* wavefront shininess is from [0, 1000], so scale for OpenGL */
  367.       model->materials[nummaterials].shininess /= 1000.0;
  368.       model->materials[nummaterials].shininess *= 128.0;
  369.       break;
  370.     case 'K':
  371.       switch(buf[1]) {
  372.       case 'd':
  373.     fscanf(file, "%f %f %f",
  374.            &model->materials[nummaterials].diffuse[0],
  375.            &model->materials[nummaterials].diffuse[1],
  376.            &model->materials[nummaterials].diffuse[2]);
  377.     break;
  378.       case 's':
  379.     fscanf(file, "%f %f %f",
  380.            &model->materials[nummaterials].specular[0],
  381.            &model->materials[nummaterials].specular[1],
  382.            &model->materials[nummaterials].specular[2]);
  383.     break;
  384.       case 'a':
  385.     fscanf(file, "%f %f %f",
  386.            &model->materials[nummaterials].ambient[0],
  387.            &model->materials[nummaterials].ambient[1],
  388.            &model->materials[nummaterials].ambient[2]);
  389.     break;
  390.       default:
  391.     /* eat up rest of line */
  392.     fgets(buf, sizeof(buf), file);
  393.     break;
  394.       }
  395.       break;
  396.     default:
  397.       /* eat up rest of line */
  398.       fgets(buf, sizeof(buf), file);
  399.       break;
  400.     }
  401.   }
  402. }
  403.  
  404. /* _glmWriteMTL: write a wavefront material library file
  405.  *
  406.  * model      - properly initialized GLMmodel structure
  407.  * modelpath  - pathname of the model being written
  408.  * mtllibname - name of the material library to be written
  409.  */
  410. static GLvoid
  411. _glmWriteMTL(GLMmodel* model, char* modelpath, char* mtllibname)
  412. {
  413.   FILE* file;
  414.   char* dir;
  415.   char* filename;
  416.   GLMmaterial* material;
  417.   GLuint i;
  418.  
  419.   dir = _glmDirName(modelpath);
  420.   filename = (char*)malloc(sizeof(char) * (strlen(dir) + strlen(mtllibname)));
  421.   strcpy(filename, dir);
  422.   strcat(filename, mtllibname);
  423.   free(dir);
  424.  
  425.   /* open the file */
  426.   file = fopen(filename, "w");
  427.   if (!file) {
  428.     fprintf(stderr, "_glmWriteMTL() failed: can't open file \"%s\".\n",
  429.         filename);
  430.     exit(1);
  431.   }
  432.   free(filename);
  433.  
  434.   /* spit out a header */
  435.   fprintf(file, "#  \n");
  436.   fprintf(file, "#  Wavefront MTL generated by GLM library\n");
  437.   fprintf(file, "#  \n");
  438.   fprintf(file, "#  GLM library copyright (C) 1997 by Nate Robins\n");
  439.   fprintf(file, "#  email: ndr@pobox.com\n");
  440.   fprintf(file, "#  www:   http://www.pobox.com/~ndr\n");
  441.   fprintf(file, "#  \n\n");
  442.  
  443.   for (i = 0; i < model->nummaterials; i++) {
  444.     material = &model->materials[i];
  445.     fprintf(file, "newmtl %s\n", material->name);
  446.     fprintf(file, "Ka %f %f %f\n", 
  447.         material->ambient[0], material->ambient[1], material->ambient[2]);
  448.     fprintf(file, "Kd %f %f %f\n", 
  449.         material->diffuse[0], material->diffuse[1], material->diffuse[2]);
  450.     fprintf(file, "Ks %f %f %f\n", 
  451.         material->specular[0],material->specular[1],material->specular[2]);
  452.     fprintf(file, "Ns %f\n", material->shininess);
  453.     fprintf(file, "\n");
  454.   }
  455. }
  456.  
  457.  
  458. /* _glmFirstPass: first pass at a Wavefront OBJ file that gets all the
  459.  * statistics of the model (such as #vertices, #normals, etc)
  460.  *
  461.  * model - properly initialized GLMmodel structure
  462.  * file  - (fopen'd) file descriptor 
  463.  */
  464. static GLvoid
  465. _glmFirstPass(GLMmodel* model, FILE* file) 
  466. {
  467.   GLuint    numvertices;        /* number of vertices in model */
  468.   GLuint    numnormals;            /* number of normals in model */
  469.   GLuint    numtexcoords;        /* number of texcoords in model */
  470.   GLuint    numtriangles;        /* number of triangles in model */
  471.   GLMgroup* group;            /* current group */
  472.   unsigned  v, n, t;
  473.   char      buf[128];
  474.  
  475.   /* make a default group */
  476.   group = _glmAddGroup(model, "default");
  477.  
  478.   numvertices = numnormals = numtexcoords = numtriangles = 0;
  479.   while(fscanf(file, "%s", buf) != EOF) {
  480.     switch(buf[0]) {
  481.     case '#':                /* comment */
  482.       /* eat up rest of line */
  483.       fgets(buf, sizeof(buf), file);
  484.       break;
  485.     case 'v':                /* v, vn, vt */
  486.       switch(buf[1]) {
  487.       case '\0':            /* vertex */
  488.     /* eat up rest of line */
  489.     fgets(buf, sizeof(buf), file);
  490.     numvertices++;
  491.     break;
  492.       case 'n':                /* normal */
  493.     /* eat up rest of line */
  494.     fgets(buf, sizeof(buf), file);
  495.     numnormals++;
  496.     break;
  497.       case 't':                /* texcoord */
  498.     /* eat up rest of line */
  499.     fgets(buf, sizeof(buf), file);
  500.     numtexcoords++;
  501.     break;
  502.       default:
  503.     printf("_glmFirstPass(): Unknown token \"%s\".\n", buf);
  504.     exit(1);
  505.     break;
  506.       }
  507.       break;
  508.     case 'm':
  509.       fgets(buf, sizeof(buf), file);
  510.       sscanf(buf, "%s %s", buf, buf);
  511.       model->mtllibname = stralloc(buf);
  512.       _glmReadMTL(model, buf);
  513.       break;
  514.     case 'u':
  515.       /* eat up rest of line */
  516.       fgets(buf, sizeof(buf), file);
  517.       break;
  518.     case 'g':                /* group */
  519.       /* eat up rest of line */
  520.       fgets(buf, sizeof(buf), file);
  521.       sscanf(buf, "%s", buf);
  522.       group = _glmAddGroup(model, buf);
  523.       break;
  524.     case 'f':                /* face */
  525.       v = n = t = 0;
  526.       fscanf(file, "%s", buf);
  527.       /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
  528.       if (strstr(buf, "//")) {
  529.     /* v//n */
  530.     sscanf(buf, "%d//%d", &v, &n);
  531.     fscanf(file, "%d//%d", &v, &n);
  532.     fscanf(file, "%d//%d", &v, &n);
  533.     numtriangles++;
  534.     group->numtriangles++;
  535.     while(fscanf(file, "%d//%d", &v, &n) > 0) {
  536.       numtriangles++;
  537.       group->numtriangles++;
  538.     }
  539.       } else if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3) {
  540.     /* v/t/n */
  541.     fscanf(file, "%d/%d/%d", &v, &t, &n);
  542.     fscanf(file, "%d/%d/%d", &v, &t, &n);
  543.     numtriangles++;
  544.     group->numtriangles++;
  545.     while(fscanf(file, "%d/%d/%d", &v, &t, &n) > 0) {
  546.       numtriangles++;
  547.       group->numtriangles++;
  548.     }
  549.       } else if (sscanf(buf, "%d/%d", &v, &t) == 2) {
  550.     /* v/t */
  551.     fscanf(file, "%d/%d", &v, &t);
  552.     fscanf(file, "%d/%d", &v, &t);
  553.     numtriangles++;
  554.     group->numtriangles++;
  555.     while(fscanf(file, "%d/%d", &v, &t) > 0) {
  556.       numtriangles++;
  557.       group->numtriangles++;
  558.     }
  559.       } else {
  560.     /* v */
  561.     fscanf(file, "%d", &v);
  562.     fscanf(file, "%d", &v);
  563.     numtriangles++;
  564.     group->numtriangles++;
  565.     while(fscanf(file, "%d", &v) > 0) {
  566.       numtriangles++;
  567.       group->numtriangles++;
  568.     }
  569.       }
  570.       break;
  571.  
  572.     default:
  573.       /* eat up rest of line */
  574.       fgets(buf, sizeof(buf), file);
  575.       break;
  576.     }
  577.   }
  578.  
  579. #if 0
  580.   /* announce the model statistics */
  581.   printf(" Vertices: %d\n", numvertices);
  582.   printf(" Normals: %d\n", numnormals);
  583.   printf(" Texcoords: %d\n", numtexcoords);
  584.   printf(" Triangles: %d\n", numtriangles);
  585.   printf(" Groups: %d\n", model->numgroups);
  586. #endif
  587.  
  588.   /* set the stats in the model structure */
  589.   model->numvertices  = numvertices;
  590.   model->numnormals   = numnormals;
  591.   model->numtexcoords = numtexcoords;
  592.   model->numtriangles = numtriangles;
  593.  
  594.   /* allocate memory for the triangles in each group */
  595.   group = model->groups;
  596.   while(group) {
  597.     group->triangles = (GLuint*)malloc(sizeof(GLuint) * group->numtriangles);
  598.     group->numtriangles = 0;
  599.     group = group->next;
  600.   }
  601. }
  602.  
  603. /* _glmSecondPass: second pass at a Wavefront OBJ file that gets all
  604.  * the data.
  605.  *
  606.  * model - properly initialized GLMmodel structure
  607.  * file  - (fopen'd) file descriptor 
  608.  */
  609. static GLvoid
  610. _glmSecondPass(GLMmodel* model, FILE* file) 
  611. {
  612.   GLuint    numvertices;        /* number of vertices in model */
  613.   GLuint    numnormals;            /* number of normals in model */
  614.   GLuint    numtexcoords;        /* number of texcoords in model */
  615.   GLuint    numtriangles;        /* number of triangles in model */
  616.   GLfloat*  vertices;            /* array of vertices  */
  617.   GLfloat*  normals;            /* array of normals */
  618.   GLfloat*  texcoords;            /* array of texture coordinates */
  619.   GLMgroup* group;            /* current group pointer */
  620.   GLuint    material;            /* current material */
  621.   GLuint    v, n, t;
  622.   char      buf[128];
  623.  
  624.   /* set the pointer shortcuts */
  625.   vertices     = model->vertices;
  626.   normals      = model->normals;
  627.   texcoords    = model->texcoords;
  628.   group        = model->groups;
  629.  
  630.   /* on the second pass through the file, read all the data into the
  631.      allocated arrays */
  632.   numvertices = numnormals = numtexcoords = 1;
  633.   numtriangles = 0;
  634.   material = 0;
  635.   while(fscanf(file, "%s", buf) != EOF) {
  636.     switch(buf[0]) {
  637.     case '#':                /* comment */
  638.       /* eat up rest of line */
  639.       fgets(buf, sizeof(buf), file);
  640.       break;
  641.     case 'v':                /* v, vn, vt */
  642.       switch(buf[1]) {
  643.       case '\0':            /* vertex */
  644.     fscanf(file, "%f %f %f", 
  645.            &vertices[3 * numvertices + X], 
  646.            &vertices[3 * numvertices + Y], 
  647.            &vertices[3 * numvertices + Z]);
  648.     numvertices++;
  649.     break;
  650.       case 'n':                /* normal */
  651.     fscanf(file, "%f %f %f", 
  652.            &normals[3 * numnormals + X],
  653.            &normals[3 * numnormals + Y], 
  654.            &normals[3 * numnormals + Z]);
  655.     numnormals++;
  656.     break;
  657.       case 't':                /* texcoord */
  658.     fscanf(file, "%f %f", 
  659.            &texcoords[2 * numtexcoords + X],
  660.            &texcoords[2 * numtexcoords + Y]);
  661.     numtexcoords++;
  662.     break;
  663.       }
  664.       break;
  665.     case 'u':
  666.       fgets(buf, sizeof(buf), file);
  667.       sscanf(buf, "%s %s", buf, buf);
  668.       group->material = material = _glmFindMaterial(model, buf);
  669.       break;
  670.     case 'g':                /* group */
  671.       /* eat up rest of line */
  672.       fgets(buf, sizeof(buf), file);
  673.       sscanf(buf, "%s", buf);
  674.       group = _glmFindGroup(model, buf);
  675.       group->material = material;
  676.       break;
  677.     case 'f':                /* face */
  678.       v = n = t = 0;
  679.       fscanf(file, "%s", buf);
  680.       /* can be one of %d, %d//%d, %d/%d, %d/%d/%d %d//%d */
  681.       if (strstr(buf, "//")) {
  682.     /* v//n */
  683.     sscanf(buf, "%d//%d", &v, &n);
  684.     T(numtriangles).vindices[0] = v;
  685.     T(numtriangles).nindices[0] = n;
  686.     fscanf(file, "%d//%d", &v, &n);
  687.     T(numtriangles).vindices[1] = v;
  688.     T(numtriangles).nindices[1] = n;
  689.     fscanf(file, "%d//%d", &v, &n);
  690.     T(numtriangles).vindices[2] = v;
  691.     T(numtriangles).nindices[2] = n;
  692.     group->triangles[group->numtriangles++] = numtriangles;
  693.     numtriangles++;
  694.     while(fscanf(file, "%d//%d", &v, &n) > 0) {
  695.       T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
  696.       T(numtriangles).nindices[0] = T(numtriangles-1).nindices[0];
  697.       T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
  698.       T(numtriangles).nindices[1] = T(numtriangles-1).nindices[2];
  699.       T(numtriangles).vindices[2] = v;
  700.       T(numtriangles).nindices[2] = n;
  701.       group->triangles[group->numtriangles++] = numtriangles;
  702.       numtriangles++;
  703.     }
  704.       } else if (sscanf(buf, "%d/%d/%d", &v, &t, &n) == 3) {
  705.     /* v/t/n */
  706.     T(numtriangles).vindices[0] = v;
  707.     T(numtriangles).tindices[0] = t;
  708.     T(numtriangles).nindices[0] = n;
  709.     fscanf(file, "%d/%d/%d", &v, &t, &n);
  710.     T(numtriangles).vindices[1] = v;
  711.     T(numtriangles).tindices[1] = t;
  712.     T(numtriangles).nindices[1] = n;
  713.     fscanf(file, "%d/%d/%d", &v, &t, &n);
  714.     T(numtriangles).vindices[2] = v;
  715.     T(numtriangles).tindices[2] = t;
  716.     T(numtriangles).nindices[2] = n;
  717.     group->triangles[group->numtriangles++] = numtriangles;
  718.     numtriangles++;
  719.     while(fscanf(file, "%d/%d/%d", &v, &t, &n) > 0) {
  720.       T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
  721.       T(numtriangles).tindices[0] = T(numtriangles-1).tindices[0];
  722.       T(numtriangles).nindices[0] = T(numtriangles-1).nindices[0];
  723.       T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
  724.       T(numtriangles).tindices[1] = T(numtriangles-1).tindices[2];
  725.       T(numtriangles).nindices[1] = T(numtriangles-1).nindices[2];
  726.       T(numtriangles).vindices[2] = v;
  727.       T(numtriangles).tindices[2] = t;
  728.       T(numtriangles).nindices[2] = n;
  729.       group->triangles[group->numtriangles++] = numtriangles;
  730.       numtriangles++;
  731.     }
  732.       } else if (sscanf(buf, "%d/%d", &v, &t) == 2) {
  733.     /* v/t */
  734.     T(numtriangles).vindices[0] = v;
  735.     T(numtriangles).tindices[0] = t;
  736.     fscanf(file, "%d/%d", &v, &t);
  737.     T(numtriangles).vindices[1] = v;
  738.     T(numtriangles).tindices[1] = t;
  739.     fscanf(file, "%d/%d", &v, &t);
  740.     T(numtriangles).vindices[2] = v;
  741.     T(numtriangles).tindices[2] = t;
  742.     group->triangles[group->numtriangles++] = numtriangles;
  743.     numtriangles++;
  744.     while(fscanf(file, "%d/%d", &v, &t) > 0) {
  745.       T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
  746.       T(numtriangles).tindices[0] = T(numtriangles-1).tindices[0];
  747.       T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
  748.       T(numtriangles).tindices[1] = T(numtriangles-1).tindices[2];
  749.       T(numtriangles).vindices[2] = v;
  750.       T(numtriangles).tindices[2] = t;
  751.       group->triangles[group->numtriangles++] = numtriangles;
  752.       numtriangles++;
  753.     }
  754.       } else {
  755.     /* v */
  756.     sscanf(buf, "%d", &v);
  757.     T(numtriangles).vindices[0] = v;
  758.     fscanf(file, "%d", &v);
  759.     T(numtriangles).vindices[1] = v;
  760.     fscanf(file, "%d", &v);
  761.     T(numtriangles).vindices[2] = v;
  762.     group->triangles[group->numtriangles++] = numtriangles;
  763.     numtriangles++;
  764.     while(fscanf(file, "%d", &v) > 0) {
  765.       T(numtriangles).vindices[0] = T(numtriangles-1).vindices[0];
  766.       T(numtriangles).vindices[1] = T(numtriangles-1).vindices[2];
  767.       T(numtriangles).vindices[2] = v;
  768.       group->triangles[group->numtriangles++] = numtriangles;
  769.       numtriangles++;
  770.     }
  771.       }
  772.       break;
  773.  
  774.     default:
  775.       /* eat up rest of line */
  776.       fgets(buf, sizeof(buf), file);
  777.       break;
  778.     }
  779.   }
  780.  
  781. #if 0
  782.   /* announce the memory requirements */
  783.   printf(" Memory: %d bytes\n",
  784.      numvertices  * 3*sizeof(GLfloat) +
  785.      numnormals   * 3*sizeof(GLfloat) * (numnormals ? 1 : 0) +
  786.      numtexcoords * 3*sizeof(GLfloat) * (numtexcoords ? 1 : 0) +
  787.      numtriangles * sizeof(GLMtriangle));
  788. #endif
  789. }
  790.  
  791.  
  792.  
  793.  
  794. /* public functions */
  795.  
  796. /* glmUnitize: "unitize" a model by translating it to the origin and
  797.  * scaling it to fit in a unit cube around the origin.  Returns the
  798.  * scalefactor used.
  799.  *
  800.  * model - properly initialized GLMmodel structure 
  801.  */
  802. GLfloat
  803. glmUnitize(GLMmodel* model)
  804. {
  805.   GLuint  i;
  806.   GLfloat maxx, minx, maxy, miny, maxz, minz;
  807.   GLfloat cx, cy, cz, w, h, d;
  808.   GLfloat scale;
  809.  
  810.   assert(model);
  811.   assert(model->vertices);
  812.  
  813.   /* get the max/mins */
  814.   maxx = minx = model->vertices[3 + X];
  815.   maxy = miny = model->vertices[3 + Y];
  816.   maxz = minz = model->vertices[3 + Z];
  817.   for (i = 1; i <= model->numvertices; i++) {
  818.     if (maxx < model->vertices[3 * i + X])
  819.       maxx = model->vertices[3 * i + X];
  820.     if (minx > model->vertices[3 * i + X])
  821.       minx = model->vertices[3 * i + X];
  822.  
  823.     if (maxy < model->vertices[3 * i + Y])
  824.       maxy = model->vertices[3 * i + Y];
  825.     if (miny > model->vertices[3 * i + Y])
  826.       miny = model->vertices[3 * i + Y];
  827.  
  828.     if (maxz < model->vertices[3 * i + Z])
  829.       maxz = model->vertices[3 * i + Z];
  830.     if (minz > model->vertices[3 * i + Z])
  831.       minz = model->vertices[3 * i + Z];
  832.   }
  833.  
  834.   /* calculate model width, height, and depth */
  835.   w = _glmAbs(maxx) + _glmAbs(minx);
  836.   h = _glmAbs(maxy) + _glmAbs(miny);
  837.   d = _glmAbs(maxz) + _glmAbs(minz);
  838.  
  839.   /* calculate center of the model */
  840.   cx = (maxx + minx) / 2.0;
  841.   cy = (maxy + miny) / 2.0;
  842.   cz = (maxz + minz) / 2.0;
  843.  
  844.   /* calculate unitizing scale factor */
  845.   scale = 2.0 / _glmMax(_glmMax(w, h), d);
  846.  
  847.   /* translate around center then scale */
  848.   for (i = 1; i <= model->numvertices; i++) {
  849.     model->vertices[3 * i + X] -= cx;
  850.     model->vertices[3 * i + Y] -= cy;
  851.     model->vertices[3 * i + Z] -= cz;
  852.     model->vertices[3 * i + X] *= scale;
  853.     model->vertices[3 * i + Y] *= scale;
  854.     model->vertices[3 * i + Z] *= scale;
  855.   }
  856.  
  857.   return scale;
  858. }
  859.  
  860. /* glmDimensions: Calculates the dimensions (width, height, depth) of
  861.  * a model.
  862.  *
  863.  * model      - initialized GLMmodel structure
  864.  * dimensions - array of 3 GLfloats (GLfloat dimensions[3])
  865.  */
  866. GLvoid
  867. glmDimensions(GLMmodel* model, GLfloat* dimensions)
  868. {
  869.   GLuint i;
  870.   GLfloat maxx, minx, maxy, miny, maxz, minz;
  871.  
  872.   assert(model);
  873.   assert(model->vertices);
  874.   assert(dimensions);
  875.  
  876.   /* get the max/mins */
  877.   maxx = minx = model->vertices[3 + X];
  878.   maxy = miny = model->vertices[3 + Y];
  879.   maxz = minz = model->vertices[3 + Z];
  880.   for (i = 1; i <= model->numvertices; i++) {
  881.     if (maxx < model->vertices[3 * i + X])
  882.       maxx = model->vertices[3 * i + X];
  883.     if (minx > model->vertices[3 * i + X])
  884.       minx = model->vertices[3 * i + X];
  885.  
  886.     if (maxy < model->vertices[3 * i + Y])
  887.       maxy = model->vertices[3 * i + Y];
  888.     if (miny > model->vertices[3 * i + Y])
  889.       miny = model->vertices[3 * i + Y];
  890.  
  891.     if (maxz < model->vertices[3 * i + Z])
  892.       maxz = model->vertices[3 * i + Z];
  893.     if (minz > model->vertices[3 * i + Z])
  894.       minz = model->vertices[3 * i + Z];
  895.   }
  896.  
  897.   /* calculate model width, height, and depth */
  898.   dimensions[X] = _glmAbs(maxx) + _glmAbs(minx);
  899.   dimensions[Y] = _glmAbs(maxy) + _glmAbs(miny);
  900.   dimensions[Z] = _glmAbs(maxz) + _glmAbs(minz);
  901. }
  902.  
  903. /* glmScale: Scales a model by a given amount.
  904.  * 
  905.  * model - properly initialized GLMmodel structure
  906.  * scale - scalefactor (0.5 = half as large, 2.0 = twice as large)
  907.  */
  908. GLvoid
  909. glmScale(GLMmodel* model, GLfloat scale)
  910. {
  911.   GLuint i;
  912.  
  913.   for (i = 1; i <= model->numvertices; i++) {
  914.     model->vertices[3 * i + X] *= scale;
  915.     model->vertices[3 * i + Y] *= scale;
  916.     model->vertices[3 * i + Z] *= scale;
  917.   }
  918. }
  919.  
  920. /* glmReverseWinding: Reverse the polygon winding for all polygons in
  921.  * this model.  Default winding is counter-clockwise.  Also changes
  922.  * the direction of the normals.
  923.  * 
  924.  * model - properly initialized GLMmodel structure 
  925.  */
  926. GLvoid
  927. glmReverseWinding(GLMmodel* model)
  928. {
  929.   GLuint i, swap;
  930.  
  931.   assert(model);
  932.  
  933.   for (i = 0; i < model->numtriangles; i++) {
  934.     swap = T(i).vindices[0];
  935.     T(i).vindices[0] = T(i).vindices[2];
  936.     T(i).vindices[2] = swap;
  937.  
  938.     if (model->numnormals) {
  939.       swap = T(i).nindices[0];
  940.       T(i).nindices[0] = T(i).nindices[2];
  941.       T(i).nindices[2] = swap;
  942.     }
  943.  
  944.     if (model->numtexcoords) {
  945.       swap = T(i).tindices[0];
  946.       T(i).tindices[0] = T(i).tindices[2];
  947.       T(i).tindices[2] = swap;
  948.     }
  949.   }
  950.  
  951.   /* reverse facet normals */
  952.   for (i = 1; i <= model->numfacetnorms; i++) {
  953.     model->facetnorms[3 * i + X] = -model->facetnorms[3 * i + X];
  954.     model->facetnorms[3 * i + Y] = -model->facetnorms[3 * i + Y];
  955.     model->facetnorms[3 * i + Z] = -model->facetnorms[3 * i + Z];
  956.   }
  957.  
  958.   /* reverse vertex normals */
  959.   for (i = 1; i <= model->numnormals; i++) {
  960.     model->normals[3 * i + X] = -model->normals[3 * i + X];
  961.     model->normals[3 * i + Y] = -model->normals[3 * i + Y];
  962.     model->normals[3 * i + Z] = -model->normals[3 * i + Z];
  963.   }
  964. }
  965.  
  966. /* glmFacetNormals: Generates facet normals for a model (by taking the
  967.  * cross product of the two vectors derived from the sides of each
  968.  * triangle).  Assumes a counter-clockwise winding.
  969.  *
  970.  * model - initialized GLMmodel structure
  971.  */
  972. GLvoid
  973. glmFacetNormals(GLMmodel* model)
  974. {
  975.   GLuint  i;
  976.   GLfloat u[3];
  977.   GLfloat v[3];
  978.   
  979.   assert(model);
  980.   assert(model->vertices);
  981.  
  982.   /* clobber any old facetnormals */
  983.   if (model->facetnorms)
  984.     free(model->facetnorms);
  985.  
  986.   /* allocate memory for the new facet normals */
  987.   model->numfacetnorms = model->numtriangles;
  988.   model->facetnorms = (GLfloat*)malloc(sizeof(GLfloat) *
  989.                        3 * (model->numfacetnorms + 1));
  990.  
  991.   for (i = 0; i < model->numtriangles; i++) {
  992.     model->triangles[i].findex = i+1;
  993.  
  994.     u[X] = model->vertices[3 * T(i).vindices[1] + X] -
  995.            model->vertices[3 * T(i).vindices[0] + X];
  996.     u[Y] = model->vertices[3 * T(i).vindices[1] + Y] -
  997.            model->vertices[3 * T(i).vindices[0] + Y];
  998.     u[Z] = model->vertices[3 * T(i).vindices[1] + Z] -
  999.            model->vertices[3 * T(i).vindices[0] + Z];
  1000.  
  1001.     v[X] = model->vertices[3 * T(i).vindices[2] + X] -
  1002.            model->vertices[3 * T(i).vindices[0] + X];
  1003.     v[Y] = model->vertices[3 * T(i).vindices[2] + Y] -
  1004.            model->vertices[3 * T(i).vindices[0] + Y];
  1005.     v[Z] = model->vertices[3 * T(i).vindices[2] + Z] -
  1006.            model->vertices[3 * T(i).vindices[0] + Z];
  1007.  
  1008.     _glmCross(u, v, &model->facetnorms[3 * (i+1)]);
  1009.     _glmNormalize(&model->facetnorms[3 * (i+1)]);
  1010.   }
  1011. }
  1012.  
  1013. /* glmVertexNormals: Generates smooth vertex normals for a model.
  1014.  * First builds a list of all the triangles each vertex is in.  Then
  1015.  * loops through each vertex in the the list averaging all the facet
  1016.  * normals of the triangles each vertex is in.  Finally, sets the
  1017.  * normal index in the triangle for the vertex to the generated smooth
  1018.  * normal.  If the dot product of a facet normal and the facet normal
  1019.  * associated with the first triangle in the list of triangles the
  1020.  * current vertex is in is greater than the cosine of the angle
  1021.  * parameter to the function, that facet normal is not added into the
  1022.  * average normal calculation and the corresponding vertex is given
  1023.  * the facet normal.  This tends to preserve hard edges.  The angle to
  1024.  * use depends on the model, but 90 degrees is usually a good start.
  1025.  *
  1026.  * model - initialized GLMmodel structure
  1027.  * angle - maximum angle (in degrees) to smooth across
  1028.  */
  1029. GLvoid
  1030. glmVertexNormals(GLMmodel* model, GLfloat angle)
  1031. {
  1032.   GLMnode*  node;
  1033.   GLMnode*  tail;
  1034.   GLMnode** members;
  1035.   GLfloat*  normals;
  1036.   GLuint    numnormals;
  1037.   GLfloat   average[3];
  1038.   GLfloat   dot, cos_angle;
  1039.   GLuint    i, avg;
  1040.  
  1041.   assert(model);
  1042.   assert(model->facetnorms);
  1043.  
  1044.   /* calculate the cosine of the angle (in degrees) */
  1045.   cos_angle = cos(angle * M_PI / 180.0);
  1046.  
  1047.   /* nuke any previous normals */
  1048.   if (model->normals)
  1049.     free(model->normals);
  1050.  
  1051.   /* allocate space for new normals */
  1052.   model->numnormals = model->numtriangles * 3; /* 3 normals per triangle */
  1053.   model->normals = (GLfloat*)malloc(sizeof(GLfloat)* 3* (model->numnormals+1));
  1054.  
  1055.   /* allocate a structure that will hold a linked list of triangle
  1056.      indices for each vertex */
  1057.   members = (GLMnode**)malloc(sizeof(GLMnode*) * (model->numvertices + 1));
  1058.   for (i = 1; i <= model->numvertices; i++)
  1059.     members[i] = NULL;
  1060.   
  1061.   /* for every triangle, create a node for each vertex in it */
  1062.   for (i = 0; i < model->numtriangles; i++) {
  1063.     node = (GLMnode*)malloc(sizeof(GLMnode));
  1064.     node->index = i;
  1065.     node->next  = members[T(i).vindices[0]];
  1066.     members[T(i).vindices[0]] = node;
  1067.  
  1068.     node = (GLMnode*)malloc(sizeof(GLMnode));
  1069.     node->index = i;
  1070.     node->next  = members[T(i).vindices[1]];
  1071.     members[T(i).vindices[1]] = node;
  1072.  
  1073.     node = (GLMnode*)malloc(sizeof(GLMnode));
  1074.     node->index = i;
  1075.     node->next  = members[T(i).vindices[2]];
  1076.     members[T(i).vindices[2]] = node;
  1077.   }
  1078.  
  1079.   /* calculate the average normal for each vertex */
  1080.   numnormals = 1;
  1081.   for (i = 1; i <= model->numvertices; i++) {
  1082.     /* calculate an average normal for this vertex by averaging the
  1083.        facet normal of every triangle this vertex is in */
  1084.     node = members[i];
  1085.     if (!node)
  1086.       fprintf(stderr, "glmVertexNormals(): vertex w/o a triangle\n");
  1087.     average[0] = 0.0; average[1] = 0.0; average[2] = 0.0;
  1088.     avg = 0;
  1089.     while (node) {
  1090.       /* only average if the dot product of the angle between the two
  1091.          facet normals is greater than the cosine of the threshold
  1092.          angle -- or, said another way, the angle between the two
  1093.          facet normals is less than (or equal to) the threshold angle */
  1094.       dot = _glmDot(&model->facetnorms[3 * T(node->index).findex],
  1095.              &model->facetnorms[3 * T(members[i]->index).findex]);
  1096.       if (dot > cos_angle) {
  1097.     node->averaged = GL_TRUE;
  1098.     average[0] += model->facetnorms[3 * T(node->index).findex + 0];
  1099.     average[1] += model->facetnorms[3 * T(node->index).findex + 1];
  1100.     average[2] += model->facetnorms[3 * T(node->index).findex + 2];
  1101.     avg = 1;            /* we averaged at least one normal! */
  1102.       } else {
  1103.     node->averaged = GL_FALSE;
  1104.       }
  1105.       node = node->next;
  1106.     }
  1107.  
  1108.     if (avg) {
  1109.       /* normalize the averaged normal */
  1110.       _glmNormalize(average);
  1111.  
  1112.       /* add the normal to the vertex normals list */
  1113.       model->normals[3 * numnormals + 0] = average[0];
  1114.       model->normals[3 * numnormals + 1] = average[1];
  1115.       model->normals[3 * numnormals + 2] = average[2];
  1116.       avg = numnormals;
  1117.       numnormals++;
  1118.     }
  1119.  
  1120.     /* set the normal of this vertex in each triangle it is in */
  1121.     node = members[i];
  1122.     while (node) {
  1123.       if (node->averaged) {
  1124.     /* if this node was averaged, use the average normal */
  1125.     if (T(node->index).vindices[0] == i)
  1126.       T(node->index).nindices[0] = avg;
  1127.     else if (T(node->index).vindices[1] == i)
  1128.       T(node->index).nindices[1] = avg;
  1129.     else if (T(node->index).vindices[2] == i)
  1130.       T(node->index).nindices[2] = avg;
  1131.       } else {
  1132.     /* if this node wasn't averaged, use the facet normal */
  1133.     model->normals[3 * numnormals + 0] = 
  1134.       model->facetnorms[3 * T(node->index).findex + 0];
  1135.     model->normals[3 * numnormals + 1] = 
  1136.       model->facetnorms[3 * T(node->index).findex + 1];
  1137.     model->normals[3 * numnormals + 2] = 
  1138.       model->facetnorms[3 * T(node->index).findex + 2];
  1139.     if (T(node->index).vindices[0] == i)
  1140.       T(node->index).nindices[0] = numnormals;
  1141.     else if (T(node->index).vindices[1] == i)
  1142.       T(node->index).nindices[1] = numnormals;
  1143.     else if (T(node->index).vindices[2] == i)
  1144.       T(node->index).nindices[2] = numnormals;
  1145.     numnormals++;
  1146.       }
  1147.       node = node->next;
  1148.     }
  1149.   }
  1150.   
  1151.   model->numnormals = numnormals - 1;
  1152.  
  1153.   /* free the member information */
  1154.   for (i = 1; i <= model->numvertices; i++) {
  1155.     node = members[i];
  1156.     while (node) {
  1157.       tail = node;
  1158.       node = node->next;
  1159.       free(tail);
  1160.     }
  1161.   }
  1162.   free(members);
  1163.  
  1164.   /* pack the normals array (we previously allocated the maximum
  1165.      number of normals that could possibly be created (numtriangles *
  1166.      3), so get rid of some of them (usually alot unless none of the
  1167.      facet normals were averaged)) */
  1168.   normals = model->normals;
  1169.   model->normals = (GLfloat*)malloc(sizeof(GLfloat)* 3* (model->numnormals+1));
  1170.   for (i = 1; i <= model->numnormals; i++) {
  1171.     model->normals[3 * i + 0] = normals[3 * i + 0];
  1172.     model->normals[3 * i + 1] = normals[3 * i + 1];
  1173.     model->normals[3 * i + 2] = normals[3 * i + 2];
  1174.   }
  1175.   free(normals);
  1176.  
  1177.   printf("glmVertexNormals(): %d normals generated\n", model->numnormals);
  1178. }
  1179.  
  1180.  
  1181. /* glmLinearTexture: Generates texture coordinates according to a
  1182.  * linear projection of the texture map.  It generates these by
  1183.  * linearly mapping the vertices onto a square.
  1184.  *
  1185.  * model - pointer to initialized GLMmodel structure
  1186.  */
  1187. GLvoid
  1188. glmLinearTexture(GLMmodel* model)
  1189. {
  1190.   GLMgroup *group;
  1191.   GLfloat dimensions[3];
  1192.   GLfloat x, y, scalefactor;
  1193.   GLuint i;
  1194.   
  1195.   assert(model);
  1196.  
  1197.   if (model->texcoords)
  1198.     free(model->texcoords);
  1199.   model->numtexcoords = model->numvertices;
  1200.   model->texcoords=(GLfloat*)malloc(sizeof(GLfloat)*2*(model->numtexcoords+1));
  1201.   
  1202.   glmDimensions(model, dimensions);
  1203.   scalefactor = 2.0 / 
  1204.     _glmAbs(_glmMax(_glmMax(dimensions[0], dimensions[1]), dimensions[2]));
  1205.  
  1206.   /* do the calculations */
  1207.   for(i = 1; i <= model->numvertices; i++) {
  1208.     x = model->vertices[3 * i + 0] * scalefactor;
  1209.     y = model->vertices[3 * i + 2] * scalefactor;
  1210.     model->texcoords[2 * i + 0] = (x + 1.0) / 2.0;
  1211.     model->texcoords[2 * i + 1] = (y + 1.0) / 2.0;
  1212.   }
  1213.   
  1214.   /* go through and put texture coordinate indices in all the triangles */
  1215.   group = model->groups;
  1216.   while(group) {
  1217.     for(i = 0; i < group->numtriangles; i++) {
  1218.       T(group->triangles[i]).tindices[0] = T(group->triangles[i]).vindices[0];
  1219.       T(group->triangles[i]).tindices[1] = T(group->triangles[i]).vindices[1];
  1220.       T(group->triangles[i]).tindices[2] = T(group->triangles[i]).vindices[2];
  1221.     }    
  1222.     group = group->next;
  1223.   }
  1224.  
  1225. #if 0
  1226.   printf("glmLinearTexture(): generated %d linear texture coordinates\n",
  1227.       model->numtexcoords);
  1228. #endif
  1229. }
  1230.  
  1231. /* glmSpheremapTexture: Generates texture coordinates according to a
  1232.  * spherical projection of the texture map.  Sometimes referred to as
  1233.  * spheremap, or reflection map texture coordinates.  It generates
  1234.  * these by using the normal to calculate where that vertex would map
  1235.  * onto a sphere.  Since it is impossible to map something flat
  1236.  * perfectly onto something spherical, there is distortion at the
  1237.  * poles.  This particular implementation causes the poles along the X
  1238.  * axis to be distorted.
  1239.  *
  1240.  * model - pointer to initialized GLMmodel structure
  1241.  */
  1242. GLvoid
  1243. glmSpheremapTexture(GLMmodel* model)
  1244. {
  1245.   GLMgroup* group;
  1246.   GLfloat theta, phi, rho, x, y, z, r;
  1247.   GLuint i;
  1248.   
  1249.   assert(model);
  1250.   assert(model->normals);
  1251.  
  1252.   if (model->texcoords)
  1253.     free(model->texcoords);
  1254.   model->numtexcoords = model->numnormals;
  1255.   model->texcoords=(GLfloat*)malloc(sizeof(GLfloat)*2*(model->numtexcoords+1));
  1256.      
  1257.   /* do the calculations */
  1258.   for (i = 1; i <= model->numnormals; i++) {
  1259.     z = model->normals[3 * i + 0];    /* re-arrange for pole distortion */
  1260.     y = model->normals[3 * i + 1];
  1261.     x = model->normals[3 * i + 2];
  1262.     r = sqrt((x * x) + (y * y));
  1263.     rho = sqrt((r * r) + (z * z));
  1264.       
  1265.     if(r == 0.0) {
  1266.     theta = 0.0;
  1267.     phi = 0.0;
  1268.     } else {
  1269.       if(z == 0.0)
  1270.     phi = M_PI / 2.0;
  1271.       else
  1272.     phi = acos(z / rho);
  1273.       
  1274. #if WE_DONT_NEED_THIS_CODE
  1275.       if(x == 0.0)
  1276.     theta = M_PI / 2.0;    /* asin(y / r); */
  1277.       else
  1278.     theta = acos(x / r);
  1279. #endif
  1280.       
  1281.       if(y == 0.0)
  1282.     theta = M_PI / 2.0;    /* acos(x / r); */
  1283.       else
  1284.     theta = asin(y / r) + (M_PI / 2.0);
  1285.     }
  1286.     
  1287.     model->texcoords[2 * i + 0] = theta / M_PI;
  1288.     model->texcoords[2 * i + 1] = phi / M_PI;
  1289.   }
  1290.   
  1291.   /* go through and put texcoord indices in all the triangles */
  1292.   group = model->groups;
  1293.   while(group) {
  1294.     for (i = 0; i < group->numtriangles; i++) {
  1295.       T(group->triangles[i]).tindices[0] = T(group->triangles[i]).nindices[0];
  1296.       T(group->triangles[i]).tindices[1] = T(group->triangles[i]).nindices[1];
  1297.       T(group->triangles[i]).tindices[2] = T(group->triangles[i]).nindices[2];
  1298.     }
  1299.     group = group->next;
  1300.   }
  1301.  
  1302. #if 0  
  1303.   printf("glmSpheremapTexture(): generated %d spheremap texture coordinates\n",
  1304.      model->numtexcoords);
  1305. #endif
  1306. }
  1307.  
  1308. /* glmDelete: Deletes a GLMmodel structure.
  1309.  *
  1310.  * model - initialized GLMmodel structure
  1311.  */
  1312. GLvoid
  1313. glmDelete(GLMmodel* model)
  1314. {
  1315.   GLMgroup* group;
  1316.   GLuint i;
  1317.  
  1318.   assert(model);
  1319.  
  1320.   if (model->pathname)   free(model->pathname);
  1321.   if (model->mtllibname) free(model->mtllibname);
  1322.   if (model->vertices)   free(model->vertices);
  1323.   if (model->normals)    free(model->normals);
  1324.   if (model->texcoords)  free(model->texcoords);
  1325.   if (model->facetnorms) free(model->facetnorms);
  1326.   if (model->triangles)  free(model->triangles);
  1327.   if (model->materials) {
  1328.     for (i = 0; i < model->nummaterials; i++)
  1329.       free(model->materials[i].name);
  1330.   }
  1331.   free(model->materials);
  1332.   while(model->groups) {
  1333.     group = model->groups;
  1334.     model->groups = model->groups->next;
  1335.     free(group->name);
  1336.     free(group->triangles);
  1337.     free(group);
  1338.   }
  1339.  
  1340.   free(model);
  1341. }
  1342.  
  1343. /* glmReadOBJ: Reads a model description from a Wavefront .OBJ file.
  1344.  * Returns a pointer to the created object which should be free'd with
  1345.  * glmDelete().
  1346.  *
  1347.  * filename - name of the file containing the Wavefront .OBJ format data.  
  1348.  */
  1349. GLMmodel* 
  1350. glmReadOBJ(char* filename)
  1351. {
  1352.   GLMmodel* model;
  1353.   FILE*     file;
  1354.  
  1355.   /* open the file */
  1356.   file = fopen(filename, "r");
  1357.   if (!file) {
  1358.     fprintf(stderr, "glmReadOBJ() failed: can't open data file \"%s\".\n",
  1359.         filename);
  1360.     exit(1);
  1361.   }
  1362.  
  1363. #if 0
  1364.   /* announce the model name */
  1365.   printf("Model: %s\n", filename);
  1366. #endif
  1367.  
  1368.   /* allocate a new model */
  1369.   model = (GLMmodel*)malloc(sizeof(GLMmodel));
  1370.   model->pathname      = stralloc(filename);
  1371.   model->mtllibname    = NULL;
  1372.   model->numvertices   = 0;
  1373.   model->vertices      = NULL;
  1374.   model->numnormals    = 0;
  1375.   model->normals       = NULL;
  1376.   model->numtexcoords  = 0;
  1377.   model->texcoords     = NULL;
  1378.   model->numfacetnorms = 0;
  1379.   model->facetnorms    = NULL;
  1380.   model->numtriangles  = 0;
  1381.   model->triangles     = NULL;
  1382.   model->nummaterials  = 0;
  1383.   model->materials     = NULL;
  1384.   model->numgroups     = 0;
  1385.   model->groups        = NULL;
  1386.   model->position[0]   = 0.0;
  1387.   model->position[1]   = 0.0;
  1388.   model->position[2]   = 0.0;
  1389.  
  1390.   /* make a first pass through the file to get a count of the number
  1391.      of vertices, normals, texcoords & triangles */
  1392.   _glmFirstPass(model, file);
  1393.  
  1394.   /* allocate memory */
  1395.   model->vertices = (GLfloat*)malloc(sizeof(GLfloat) *
  1396.                      3 * (model->numvertices + 1));
  1397.   model->triangles = (GLMtriangle*)malloc(sizeof(GLMtriangle) *
  1398.                       model->numtriangles);
  1399.   if (model->numnormals) {
  1400.     model->normals = (GLfloat*)malloc(sizeof(GLfloat) *
  1401.                       3 * (model->numnormals + 1));
  1402.   }
  1403.   if (model->numtexcoords) {
  1404.     model->texcoords = (GLfloat*)malloc(sizeof(GLfloat) *
  1405.                     2 * (model->numtexcoords + 1));
  1406.   }
  1407.  
  1408.   /* rewind to beginning of file and read in the data this pass */
  1409.   rewind(file);
  1410.  
  1411.   _glmSecondPass(model, file);
  1412.  
  1413.   /* close the file */
  1414.   fclose(file);
  1415.  
  1416.   return model;
  1417. }
  1418.  
  1419. /* glmWriteOBJ: Writes a model description in Wavefront .OBJ format to
  1420.  * a file.
  1421.  *
  1422.  * model    - initialized GLMmodel structure
  1423.  * filename - name of the file to write the Wavefront .OBJ format data to
  1424.  * mode     - a bitwise or of values describing what is written to the file
  1425.  *            GLM_NONE     -  render with only vertices
  1426.  *            GLM_FLAT     -  render with facet normals
  1427.  *            GLM_SMOOTH   -  render with vertex normals
  1428.  *            GLM_TEXTURE  -  render with texture coords
  1429.  *            GLM_COLOR    -  render with colors (color material)
  1430.  *            GLM_MATERIAL -  render with materials
  1431.  *            GLM_COLOR and GLM_MATERIAL should not both be specified.  
  1432.  *            GLM_FLAT and GLM_SMOOTH should not both be specified.  
  1433.  */
  1434. GLvoid
  1435. glmWriteOBJ(GLMmodel* model, char* filename, GLuint mode)
  1436. {
  1437.   GLuint    i;
  1438.   FILE*     file;
  1439.   GLMgroup* group;
  1440.  
  1441.   assert(model);
  1442.  
  1443.   /* do a bit of warning */
  1444.   if (mode & GLM_FLAT && !model->facetnorms) {
  1445.     printf("glmWriteOBJ() warning: flat normal output requested "
  1446.        "with no facet normals defined.\n");
  1447.     mode &= ~GLM_FLAT;
  1448.   }
  1449.   if (mode & GLM_SMOOTH && !model->normals) {
  1450.     printf("glmWriteOBJ() warning: smooth normal output requested "
  1451.        "with no normals defined.\n");
  1452.     mode &= ~GLM_SMOOTH;
  1453.   }
  1454.   if (mode & GLM_TEXTURE && !model->texcoords) {
  1455.     printf("glmWriteOBJ() warning: texture coordinate output requested "
  1456.        "with no texture coordinates defined.\n");
  1457.     mode &= ~GLM_TEXTURE;
  1458.   }
  1459.   if (mode & GLM_FLAT && mode & GLM_SMOOTH) {
  1460.     printf("glmWriteOBJ() warning: flat normal output requested "
  1461.        "and smooth normal output requested (using smooth).\n");
  1462.     mode &= ~GLM_FLAT;
  1463.   }
  1464.  
  1465.   /* open the file */
  1466.   file = fopen(filename, "w");
  1467.   if (!file) {
  1468.     fprintf(stderr, "glmWriteOBJ() failed: can't open file \"%s\" to write.\n",
  1469.         filename);
  1470.     exit(1);
  1471.   }
  1472.  
  1473.   /* spit out a header */
  1474.   fprintf(file, "#  \n");
  1475.   fprintf(file, "#  Wavefront OBJ generated by GLM library\n");
  1476.   fprintf(file, "#  \n");
  1477.   fprintf(file, "#  GLM library copyright (C) 1997 by Nate Robins\n");
  1478.   fprintf(file, "#  email: ndr@pobox.com\n");
  1479.   fprintf(file, "#  www:   http://www.pobox.com/~ndr\n");
  1480.   fprintf(file, "#  \n");
  1481.  
  1482.   if (mode & GLM_MATERIAL && model->mtllibname) {
  1483.     fprintf(file, "\nmtllib %s\n\n", model->mtllibname);
  1484.     _glmWriteMTL(model, filename, model->mtllibname);
  1485.   }
  1486.  
  1487.   /* spit out the vertices */
  1488.   fprintf(file, "\n");
  1489.   fprintf(file, "# %d vertices\n", model->numvertices);
  1490.   for (i = 1; i <= model->numvertices; i++) {
  1491.     fprintf(file, "v %f %f %f\n", 
  1492.         model->vertices[3 * i + 0],
  1493.         model->vertices[3 * i + 1],
  1494.         model->vertices[3 * i + 2]);
  1495.   }
  1496.  
  1497.   /* spit out the smooth/flat normals */
  1498.   if (mode & GLM_SMOOTH) {
  1499.     fprintf(file, "\n");
  1500.     fprintf(file, "# %d normals\n", model->numnormals);
  1501.     for (i = 1; i <= model->numnormals; i++) {
  1502.       fprintf(file, "vn %f %f %f\n", 
  1503.           model->normals[3 * i + 0],
  1504.           model->normals[3 * i + 1],
  1505.           model->normals[3 * i + 2]);
  1506.     }
  1507.   } else if (mode & GLM_FLAT) {
  1508.     fprintf(file, "\n");
  1509.     fprintf(file, "# %d normals\n", model->numfacetnorms);
  1510.     for (i = 1; i <= model->numnormals; i++) {
  1511.       fprintf(file, "vn %f %f %f\n", 
  1512.           model->facetnorms[3 * i + 0],
  1513.           model->facetnorms[3 * i + 1],
  1514.           model->facetnorms[3 * i + 2]);
  1515.     }
  1516.   }
  1517.  
  1518.   /* spit out the texture coordinates */
  1519.   if (mode & GLM_TEXTURE) {
  1520.     fprintf(file, "\n");
  1521.     fprintf(file, "# %d texcoords\n", model->numtexcoords);
  1522.     for (i = 1; i <= model->numtexcoords; i++) {
  1523.       fprintf(file, "vt %f %f\n", 
  1524.           model->texcoords[2 * i + 0],
  1525.           model->texcoords[2 * i + 1]);
  1526.     }
  1527.   }
  1528.  
  1529.   fprintf(file, "\n");
  1530.   fprintf(file, "# %d groups\n", model->numgroups);
  1531.   fprintf(file, "# %d faces (triangles)\n", model->numtriangles);
  1532.   fprintf(file, "\n");
  1533.  
  1534.   group = model->groups;
  1535.   while(group) {
  1536.     fprintf(file, "g %s\n", group->name);
  1537.     if (mode & GLM_MATERIAL)
  1538.       fprintf(file, "usemtl %s\n", model->materials[group->material].name);
  1539.     for (i = 0; i < group->numtriangles; i++) {
  1540.       if (mode & GLM_SMOOTH && mode & GLM_TEXTURE) {
  1541.     fprintf(file, "f %d/%d/%d %d/%d/%d %d/%d/%d\n",
  1542.         T(group->triangles[i]).vindices[0], 
  1543.         T(group->triangles[i]).nindices[0], 
  1544.         T(group->triangles[i]).tindices[0],
  1545.         T(group->triangles[i]).vindices[1],
  1546.         T(group->triangles[i]).nindices[1],
  1547.         T(group->triangles[i]).tindices[1],
  1548.         T(group->triangles[i]).vindices[2],
  1549.         T(group->triangles[i]).nindices[2],
  1550.         T(group->triangles[i]).tindices[2]);
  1551.       } else if (mode & GLM_FLAT && mode & GLM_TEXTURE) {
  1552.     fprintf(file, "f %d/%d %d/%d %d/%d\n",
  1553.         T(group->triangles[i]).vindices[0],
  1554.         T(group->triangles[i]).findex,
  1555.         T(group->triangles[i]).vindices[1],
  1556.         T(group->triangles[i]).findex,
  1557.         T(group->triangles[i]).vindices[2],
  1558.         T(group->triangles[i]).findex);
  1559.       } else if (mode & GLM_TEXTURE) {
  1560.     fprintf(file, "f %d/%d %d/%d %d/%d\n",
  1561.         T(group->triangles[i]).vindices[0],
  1562.         T(group->triangles[i]).tindices[0],
  1563.         T(group->triangles[i]).vindices[1],
  1564.         T(group->triangles[i]).tindices[1],
  1565.         T(group->triangles[i]).vindices[2],
  1566.         T(group->triangles[i]).tindices[2]);
  1567.       } else if (mode & GLM_SMOOTH) {
  1568.     fprintf(file, "f %d//%d %d//%d %d//%d\n",
  1569.         T(group->triangles[i]).vindices[0],
  1570.         T(group->triangles[i]).nindices[0],
  1571.         T(group->triangles[i]).vindices[1],
  1572.         T(group->triangles[i]).nindices[1],
  1573.         T(group->triangles[i]).vindices[2], 
  1574.         T(group->triangles[i]).nindices[2]);
  1575.       } else if (mode & GLM_FLAT) {
  1576.     fprintf(file, "f %d//%d %d//%d %d//%d\n",
  1577.         T(group->triangles[i]).vindices[0], 
  1578.         T(group->triangles[i]).findex,
  1579.         T(group->triangles[i]).vindices[1],
  1580.         T(group->triangles[i]).findex,
  1581.         T(group->triangles[i]).vindices[2],
  1582.         T(group->triangles[i]).findex);
  1583.       } else {
  1584.     fprintf(file, "f %d %d %d\n",
  1585.         T(group->triangles[i]).vindices[0],
  1586.         T(group->triangles[i]).vindices[1],
  1587.         T(group->triangles[i]).vindices[2]);
  1588.       }
  1589.     }
  1590.     fprintf(file, "\n");
  1591.     group = group->next;
  1592.   }
  1593.  
  1594.   fclose(file);
  1595. }
  1596.  
  1597. /* glmDraw: Renders the model to the current OpenGL context using the
  1598.  * mode specified.
  1599.  *
  1600.  * model    - initialized GLMmodel structure
  1601.  * mode     - a bitwise OR of values describing what is to be rendered.
  1602.  *            GLM_NONE     -  render with only vertices
  1603.  *            GLM_FLAT     -  render with facet normals
  1604.  *            GLM_SMOOTH   -  render with vertex normals
  1605.  *            GLM_TEXTURE  -  render with texture coords
  1606.  *            GLM_COLOR    -  render with colors (color material)
  1607.  *            GLM_MATERIAL -  render with materials
  1608.  *            GLM_COLOR and GLM_MATERIAL should not both be specified.  
  1609.  *            GLM_FLAT and GLM_SMOOTH should not both be specified.  
  1610.  */
  1611. GLvoid
  1612. glmDraw(GLMmodel* model, GLuint mode)
  1613. {
  1614.   GLuint i;
  1615.   GLMgroup* group;
  1616.  
  1617.   assert(model);
  1618.   assert(model->vertices);
  1619.  
  1620.   /* do a bit of warning */
  1621.   if (mode & GLM_FLAT && !model->facetnorms) {
  1622.     printf("glmDraw() warning: flat render mode requested "
  1623.        "with no facet normals defined.\n");
  1624.     mode &= ~GLM_FLAT;
  1625.   }
  1626.   if (mode & GLM_SMOOTH && !model->normals) {
  1627.     printf("glmDraw() warning: smooth render mode requested "
  1628.        "with no normals defined.\n");
  1629.     mode &= ~GLM_SMOOTH;
  1630.   }
  1631.   if (mode & GLM_TEXTURE && !model->texcoords) {
  1632.     printf("glmDraw() warning: texture render mode requested "
  1633.        "with no texture coordinates defined.\n");
  1634.     mode &= ~GLM_TEXTURE;
  1635.   }
  1636.   if (mode & GLM_FLAT && mode & GLM_SMOOTH) {
  1637.     printf("glmDraw() warning: flat render mode requested "
  1638.        "and smooth render mode requested (using smooth).\n");
  1639.     mode &= ~GLM_FLAT;
  1640.   }
  1641.   if (mode & GLM_COLOR && !model->materials) {
  1642.     printf("glmDraw() warning: color render mode requested "
  1643.        "with no materials defined.\n");
  1644.     mode &= ~GLM_COLOR;
  1645.   }
  1646.   if (mode & GLM_MATERIAL && !model->materials) {
  1647.     printf("glmDraw() warning: material render mode requested "
  1648.        "with no materials defined.\n");
  1649.     mode &= ~GLM_MATERIAL;
  1650.   }
  1651.   if (mode & GLM_COLOR && mode & GLM_MATERIAL) {
  1652.     printf("glmDraw() warning: color and material render mode requested "
  1653.        "using only material mode\n");
  1654.     mode &= ~GLM_COLOR;
  1655.   }
  1656.   if (mode & GLM_COLOR)
  1657.     glEnable(GL_COLOR_MATERIAL);
  1658.   if (mode & GLM_MATERIAL)
  1659.     glDisable(GL_COLOR_MATERIAL);
  1660.  
  1661.   glPushMatrix();
  1662.   glTranslatef(model->position[0], model->position[1], model->position[2]);
  1663.  
  1664.   glBegin(GL_TRIANGLES);
  1665.   group = model->groups;
  1666.   while (group) {
  1667.     if (mode & GLM_MATERIAL) {
  1668.       glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, 
  1669.            model->materials[group->material].ambient);
  1670.       glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, 
  1671.            model->materials[group->material].diffuse);
  1672.       glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, 
  1673.            model->materials[group->material].specular);
  1674.        glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 
  1675.             model->materials[group->material].shininess);
  1676.     }
  1677.  
  1678.     if (mode & GLM_COLOR) {
  1679.       glColor3fv(model->materials[group->material].diffuse);
  1680.     }
  1681.  
  1682.     for (i = 0; i < group->numtriangles; i++) {
  1683.       if (mode & GLM_FLAT)
  1684.     glNormal3fv(&model->facetnorms[3 * T(group->triangles[i]).findex]);
  1685.       
  1686.       if (mode & GLM_SMOOTH)
  1687.     glNormal3fv(&model->normals[3 * T(group->triangles[i]).nindices[0]]);
  1688.       if (mode & GLM_TEXTURE)
  1689.     glTexCoord2fv(&model->texcoords[2*T(group->triangles[i]).tindices[0]]);
  1690.       glVertex3fv(&model->vertices[3 * T(group->triangles[i]).vindices[0]]);
  1691. #if 0
  1692.       printf("%f %f %f\n", 
  1693.          model->vertices[3 * T(group->triangles[i]).vindices[0] + X],
  1694.          model->vertices[3 * T(group->triangles[i]).vindices[0] + Y],
  1695.          model->vertices[3 * T(group->triangles[i]).vindices[0] + Z]);
  1696. #endif
  1697.       
  1698.       if (mode & GLM_SMOOTH)
  1699.     glNormal3fv(&model->normals[3 * T(group->triangles[i]).nindices[1]]);
  1700.       if (mode & GLM_TEXTURE)
  1701.     glTexCoord2fv(&model->texcoords[2*T(group->triangles[i]).tindices[1]]);
  1702.       glVertex3fv(&model->vertices[3 * T(group->triangles[i]).vindices[1]]);
  1703. #if 0
  1704.       printf("%f %f %f\n", 
  1705.          model->vertices[3 * T(group->triangles[i]).vindices[1] + X],
  1706.          model->vertices[3 * T(group->triangles[i]).vindices[1] + Y],
  1707.          model->vertices[3 * T(group->triangles[i]).vindices[1] + Z]);
  1708. #endif
  1709.       
  1710.       if (mode & GLM_SMOOTH)
  1711.     glNormal3fv(&model->normals[3 * T(group->triangles[i]).nindices[2]]);
  1712.       if (mode & GLM_TEXTURE)
  1713.     glTexCoord2fv(&model->texcoords[2*T(group->triangles[i]).tindices[2]]);
  1714.       glVertex3fv(&model->vertices[3 * T(group->triangles[i]).vindices[2]]);
  1715. #if 0
  1716.       printf("%f %f %f\n", 
  1717.          model->vertices[3 * T(group->triangles[i]).vindices[2] + X],
  1718.          model->vertices[3 * T(group->triangles[i]).vindices[2] + Y],
  1719.          model->vertices[3 * T(group->triangles[i]).vindices[2] + Z]);
  1720. #endif
  1721.       
  1722.     }
  1723.     
  1724.     group = group->next;
  1725.   }
  1726.   glEnd();
  1727.  
  1728.   glPopMatrix();
  1729. }
  1730.  
  1731. /* glmList: Generates and returns a display list for the model using
  1732.  * the mode specified.
  1733.  *
  1734.  * model    - initialized GLMmodel structure
  1735.  * mode     - a bitwise OR of values describing what is to be rendered.
  1736.  *            GLM_NONE     -  render with only vertices
  1737.  *            GLM_FLAT     -  render with facet normals
  1738.  *            GLM_SMOOTH   -  render with vertex normals
  1739.  *            GLM_TEXTURE  -  render with texture coords
  1740.  *            GLM_COLOR    -  render with colors (color material)
  1741.  *            GLM_MATERIAL -  render with materials
  1742.  *            GLM_COLOR and GLM_MATERIAL should not both be specified.  
  1743.  *            GLM_FLAT and GLM_SMOOTH should not both be specified.  
  1744.  */
  1745. GLuint
  1746. glmList(GLMmodel* model, GLuint mode)
  1747. {
  1748.   GLuint list;
  1749.  
  1750.   list = glGenLists(1);
  1751.   glNewList(list, GL_COMPILE);
  1752.   glmDraw(model, mode);
  1753.   glEndList();
  1754.  
  1755.   return list;
  1756. }
  1757.  
  1758. /* glmWeld: eliminate (weld) vectors that are within an epsilon of
  1759.  * each other.
  1760.  *
  1761.  * model      - initialized GLMmodel structure
  1762.  * epsilon    - maximum difference between vertices
  1763.  *              ( 0.00001 is a good start for a unitized model)
  1764.  *
  1765.  */
  1766. GLvoid
  1767. glmWeld(GLMmodel* model, GLfloat epsilon)
  1768. {
  1769.   GLfloat* vectors;
  1770.   GLfloat* copies;
  1771.   GLuint   numvectors;
  1772.   GLuint   i;
  1773.  
  1774.   /* vertices */
  1775.   numvectors = model->numvertices;
  1776.   vectors    = model->vertices;
  1777.   copies = _glmWeldVectors(vectors, &numvectors, epsilon);
  1778.  
  1779.   printf("glmWeld(): %d redundant vertices.\n", 
  1780.      model->numvertices - numvectors - 1);
  1781.  
  1782.   for (i = 0; i < model->numtriangles; i++) {
  1783.     T(i).vindices[0] = (GLuint)vectors[3 * T(i).vindices[0] + 0];
  1784.     T(i).vindices[1] = (GLuint)vectors[3 * T(i).vindices[1] + 0];
  1785.     T(i).vindices[2] = (GLuint)vectors[3 * T(i).vindices[2] + 0];
  1786.   }
  1787.  
  1788.   /* free space for old vertices */
  1789.   free(vectors);
  1790.  
  1791.   /* allocate space for the new vertices */
  1792.   model->numvertices = numvectors;
  1793.   model->vertices = (GLfloat*)malloc(sizeof(GLfloat) * 
  1794.                      3 * (model->numvertices + 1));
  1795.  
  1796.   /* copy the optimized vertices into the actual vertex list */
  1797.   for (i = 1; i <= model->numvertices; i++) {
  1798.     model->vertices[3 * i + 0] = copies[3 * i + 0];
  1799.     model->vertices[3 * i + 1] = copies[3 * i + 1];
  1800.     model->vertices[3 * i + 2] = copies[3 * i + 2];
  1801.   }
  1802.  
  1803.   free(copies);
  1804. }
  1805.  
  1806.  
  1807. #if 0
  1808.   /* normals */
  1809.   if (model->numnormals) {
  1810.   numvectors = model->numnormals;
  1811.   vectors    = model->normals;
  1812.   copies = _glmOptimizeVectors(vectors, &numvectors);
  1813.  
  1814.   printf("glmOptimize(): %d redundant normals.\n", 
  1815.      model->numnormals - numvectors);
  1816.  
  1817.   for (i = 0; i < model->numtriangles; i++) {
  1818.     T(i).nindices[0] = (GLuint)vectors[3 * T(i).nindices[0] + 0];
  1819.     T(i).nindices[1] = (GLuint)vectors[3 * T(i).nindices[1] + 0];
  1820.     T(i).nindices[2] = (GLuint)vectors[3 * T(i).nindices[2] + 0];
  1821.   }
  1822.  
  1823.   /* free space for old normals */
  1824.   free(vectors);
  1825.  
  1826.   /* allocate space for the new normals */
  1827.   model->numnormals = numvectors;
  1828.   model->normals = (GLfloat*)malloc(sizeof(GLfloat) * 
  1829.                     3 * (model->numnormals + 1));
  1830.  
  1831.   /* copy the optimized vertices into the actual vertex list */
  1832.   for (i = 1; i <= model->numnormals; i++) {
  1833.     model->normals[3 * i + 0] = copies[3 * i + 0];
  1834.     model->normals[3 * i + 1] = copies[3 * i + 1];
  1835.     model->normals[3 * i + 2] = copies[3 * i + 2];
  1836.   }
  1837.  
  1838.   free(copies);
  1839.   }
  1840.  
  1841.   /* texcoords */
  1842.   if (model->numtexcoords) {
  1843.   numvectors = model->numtexcoords;
  1844.   vectors    = model->texcoords;
  1845.   copies = _glmOptimizeVectors(vectors, &numvectors);
  1846.  
  1847.   printf("glmOptimize(): %d redundant texcoords.\n", 
  1848.      model->numtexcoords - numvectors);
  1849.  
  1850.   for (i = 0; i < model->numtriangles; i++) {
  1851.     for (j = 0; j < 3; j++) {
  1852.       T(i).tindices[j] = (GLuint)vectors[3 * T(i).tindices[j] + 0];
  1853.     }
  1854.   }
  1855.  
  1856.   /* free space for old texcoords */
  1857.   free(vectors);
  1858.  
  1859.   /* allocate space for the new texcoords */
  1860.   model->numtexcoords = numvectors;
  1861.   model->texcoords = (GLfloat*)malloc(sizeof(GLfloat) * 
  1862.                       2 * (model->numtexcoords + 1));
  1863.  
  1864.   /* copy the optimized vertices into the actual vertex list */
  1865.   for (i = 1; i <= model->numtexcoords; i++) {
  1866.     model->texcoords[2 * i + 0] = copies[2 * i + 0];
  1867.     model->texcoords[2 * i + 1] = copies[2 * i + 1];
  1868.   }
  1869.  
  1870.   free(copies);
  1871.   }
  1872. #endif
  1873.  
  1874. #if 0
  1875.   /* look for unused vertices */
  1876.   /* look for unused normals */
  1877.   /* look for unused texcoords */
  1878.   for (i = 1; i <= model->numvertices; i++) {
  1879.     for (j = 0; j < model->numtriangles; i++) {
  1880.       if (T(j).vindices[0] == i || 
  1881.       T(j).vindices[1] == i || 
  1882.       T(j).vindices[1] == i)
  1883.     break;
  1884.     }
  1885.   }
  1886. #endif
  1887.